package org.example.me.handler.template;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import lombok.extern.slf4j.Slf4j;
import org.example.me.handler.context.ExcelContext;
import org.example.me.handler.context.FileContext;
import org.example.me.handler.context.ExcelContextExtend;
import org.example.me.handler.context.SheetCreator;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 通用导出模板
 * 增加了导出类和缓存，提供通用的Excel导出功能实现
 * 相比基础模板，提供了更丰富的功能，如多Sheet、多文件支持
 */
@Slf4j
public abstract class AbstractCommonExportTemplate extends AbstractBaseExcelExportTemplate {
    /**
     * 写文件入口方法
     * 将基础上下文转换为扩展上下文，调用具体导出方法，最后完成写入操作
     * 
     * @param excelContext Excel上下文信息
     */
    @Override
    public final void writeFile(ExcelContext excelContext) {
        ExcelContextExtend simpleExcelContext = new ExcelContextExtend(excelContext);
        export(simpleExcelContext);
        finish(simpleExcelContext);
    }

    /**
     * 完成Excel写入操作，关闭所有写入器
     *
     * @param excelContext Excel上下文信息
     */
    protected final void finish(ExcelContextExtend excelContext) {
        Map<String, FileContext> fileContextMap = excelContext.getFileContextMap();
        if (CollectionUtils.isEmpty(fileContextMap)) {
            return;
        }
        fileContextMap.forEach((fileName, fileContext) -> {
            ExcelWriter excelWriter = fileContext.getExcelWriter();
            if (excelWriter != null) {
                excelWriter.finish();
            }
        });
    }

    /**
     * 抽象方法，由子类实现具体的导出逻辑
     *
     * @param excelContext Excel上下文信息
     */
    protected abstract void export(ExcelContextExtend excelContext);

    /**
     * 将数据写入Excel文件（使用默认文件名和Sheet名）
     *
     * @param excelContext Excel上下文信息
     * @param dataList     数据列表
     * @param tClass       数据类型
     */
    protected void doWriteData(ExcelContextExtend excelContext, List<?> dataList, Class<?> tClass) {
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }
        SheetCreator sheetCreator = SheetCreator.builder().sheetNo(0).sheetName(getExcelType().getSheetName()).tClass(tClass).build();
        doWriteData(excelContext, getExcelType().getFileName(), dataList, sheetCreator);
    }

    /**
     * 将数据写入Excel文件（指定文件名和Sheet名）
     *
     * @param excelContext Excel上下文信息
     * @param fileName     文件名
     * @param dataList     数据列表
     * @param sheetCreator Sheet创建器，包含Sheet的各种配置信息
     */
    protected void doWriteData(ExcelContextExtend excelContext, String fileName, List<?> dataList, SheetCreator sheetCreator) {
        FileContext fileContext = getFileContext(excelContext, fileName);
        checkOrInitWriteSheet(fileContext, sheetCreator);
        doWriteData(fileContext, sheetCreator.getSheetName(), dataList);
    }

    /**
     * 将数据写入Excel文件（直接指定文件上下文和Sheet上下文）
     *
     * @param fileContext 文件上下文
     * @param sheetName   需要写的表
     * @param dataList    数据列表
     */
    protected void doWriteData(FileContext fileContext, String sheetName, List<?> dataList) {
        ExcelWriter excelWriter = fileContext.getExcelWriter();
        Map<String, WriteSheet> writeSheetMap = fileContext.getWriteSheetMap();
        WriteSheet writeSheet = writeSheetMap.get(sheetName);
        excelWriter.write(dataList, writeSheet);
    }

    /**
     * 检查Sheet是否存在，不存在则创建
     * 确保每个Sheet只被创建一次，避免重复创建
     *
     * @param fileContext  文件上下文
     * @param sheetCreator sheet创建器
     */
    private static void checkOrInitWriteSheet(FileContext fileContext, SheetCreator sheetCreator) {
        Map<String, WriteSheet> writeSheetMap = Optional.ofNullable(fileContext.getWriteSheetMap()).orElseGet(HashMap::new);
        fileContext.setWriteSheetMap(writeSheetMap);
        writeSheetMap.computeIfAbsent(sheetCreator.getSheetName(), k -> {
            // 设置Sheet编号
            sheetCreator.setSheetNo(writeSheetMap.size());
            return initWriteSheet(sheetCreator);
        });
    }

    /**
     * 获取文件上下文，如果不存在则创建新的
     * 确保每个文件只对应一个文件上下文，避免重复创建
     *
     * @param excelContext Excel上下文
     * @param fileName     文件名
     * @return 文件上下文
     */
    private static FileContext getFileContext(ExcelContextExtend excelContext, String fileName) {
        Map<String, FileContext> fileContextMap = Optional.ofNullable(excelContext.getFileContextMap()).orElseGet(HashMap::new);
        excelContext.setFileContextMap(fileContextMap);
        return fileContextMap.computeIfAbsent(fileName, k -> initFileContext(excelContext.getExcelContext(), k));
    }

    /**
     * 初始化文件上下文
     * 创建ExcelWriter并初始化文件相关配置
     *
     * @param excelContext Excel上下文
     * @param fileName     文件名
     * @return 文件上下文
     */
    protected static FileContext initFileContext(ExcelContext excelContext, String fileName) {
        return initFileContext(excelContext, fileName, null);
    }

    /**
     * 初始化文件上下文（带Sheet配置）
     * 创建ExcelWriter并初始化文件及Sheet相关配置
     *
     * @param excelContext  Excel上下文
     * @param fileName      文件名
     * @param sheetContexts Sheet配置列表
     * @return 文件上下文
     */
    protected static FileContext initFileContext(ExcelContext excelContext, String fileName, List<SheetCreator> sheetContexts) {
        // 如果不存在，则开始创建
        FileContext fileContext = new FileContext();
        String tempDirectory = initUserTempDirectory(excelContext);
        // 生成文件路径
        String filePath = tempDirectory + "/" + fileName + ".xlsx";
        addFile(excelContext, filePath);
        ExcelWriter excelWriter = EasyExcel.write(filePath).excelType(ExcelTypeEnum.XLSX).build();
        fileContext.setExcelWriter(excelWriter);
        if (sheetContexts != null) {
            // 初始化所有Sheet
            int sheetNo = 0;
            for (SheetCreator sheetContext : sheetContexts) {
                sheetContext.setSheetNo(sheetNo);
                sheetNo++;
                WriteSheet writeSheet = initWriteSheet(sheetContext);
                fileContext.addWriteSheet(writeSheet);
            }
        }
        return fileContext;
    }

    /**
     * 初始化Sheet上下文
     * 根据Sheet创建器的配置信息创建WriteSheet对象
     *
     * @param creator Sheet创建器
     * @return Sheet上下文
     */
    protected static WriteSheet initWriteSheet(SheetCreator creator) {
        ExcelWriterSheetBuilder builder = EasyExcel.writerSheet()
                .sheetNo(creator.getSheetNo())
                .sheetName(creator.getSheetName())
                .includeColumnFieldNames(creator.getIncludeColumnFieldNames());
        if (creator.getHead() != null) {
            builder.head(creator.getHead());
        } else if (creator.getTClass() != null) {
            builder.head(creator.getTClass());
        }
        if (!CollectionUtils.isEmpty(creator.getWriteHandlerList())) {
            for (WriteHandler writeHandler : creator.getWriteHandlerList()) {
                builder.registerWriteHandler(writeHandler);
            }
        }
        return builder.build();
    }
}